#include "loco.h"
#include "../maindrawscene.h"
#include "../cmainframe.h"

Track::Track()
    : mSectNode(NULL)
{

}

Track::~Track()
{

}

/// 添加指定机车到轨道上
bool Track::addLoco(Loco* ploco,SwitchState state,int dir)
{
    if(ploco == NULL || isExistLoco(ploco))
        return false;

    if(dir == 0)
        mTrackLocos[state].push_front(ploco);
    else
        mTrackLocos[state].push_back(ploco);

    ploco->setSectNode(this->getSectNode());

    return true;
}

/// 添加机车到指定机车的前面或者后面
bool Track::addLocos(std::vector<Loco*> srcLoco,Loco* decLoco)
{
    if(srcLoco.empty() ||
        decLoco == NULL ||
        decLoco->getSectNode() == NULL)
        return false;

    wxString decLog = wxT("移动机车:");
    for(int i=0;i<srcLoco.size();i++)
    {
        srcLoco[i]->setSectNode(this->getSectNode());
        decLog += wxString::Format(wxT("%s-"),srcLoco[i]->getName());
    }

    Loco* decInsertLoco = NULL;

    if((decLoco->getRightLoco() == NULL && decLoco->getLeftLoco() == NULL) ||
        (decLoco->getRightLoco() == NULL && decLoco->getLeftLoco() != NULL))
    {
        decInsertLoco = decLoco;

        std::deque<Loco*>::iterator iterdecloco;
        std::map<SwitchState,std::deque<Loco*>>::iterator itertrack = mTrackLocos.begin();
        for(;itertrack != mTrackLocos.end();++itertrack)
        {
            iterdecloco = std::find((*itertrack).second.begin(),(*itertrack).second.end(),decInsertLoco);
            if(iterdecloco != (*itertrack).second.end())
            {
                //std::reverse(srcLoco.begin(), srcLoco.end());
                (*itertrack).second.insert(iterdecloco+1,srcLoco.begin(),srcLoco.end());
                break;
            }
        }

        decLog += wxString::Format(wxT("到机车%s后面."),decInsertLoco->getName());
    }
    else
    {
        decInsertLoco = decLoco;
        Loco *lastLoco = decLoco->getLeftLoco();
        while(lastLoco != NULL)
        {
            decInsertLoco = lastLoco;
            lastLoco = lastLoco->getLeftLoco();
        }

        std::deque<Loco*>::iterator iterdecloco;
        std::map<SwitchState,std::deque<Loco*>>::iterator itertrack = mTrackLocos.begin();
        for(;itertrack != mTrackLocos.end();++itertrack)
        {
            iterdecloco = std::find((*itertrack).second.begin(),(*itertrack).second.end(),decInsertLoco);
            if(iterdecloco != (*itertrack).second.end())
            {
                (*itertrack).second.insert(iterdecloco,srcLoco.begin(),srcLoco.end());
                break;
            }
        }

        decLog += wxString::Format(wxT("到机车%s前面."),decInsertLoco->getName());
    }

    CMainFrame::getSingleton().printlog(LOG_LEVEL_INFO,decLog.ToUTF8().data());

    return true;
}

/// 分离机车
bool Track::uncouplingLoco(Loco* leftLoco,Loco *rightLoco)
{
    if((leftLoco == NULL || rightLoco == NULL) ||
        leftLoco == rightLoco ||
        mTrackLocos.empty())
        return false;

    // 连个结点必须相邻
    std::map<SwitchState,std::deque<Loco*>>::iterator itertrack = mTrackLocos.begin();
    for(;itertrack != mTrackLocos.end();++itertrack)
    {
        for(int i=0;i<(*itertrack).second.size();i++)
        {
            if((*itertrack).second[i] == leftLoco &&
                (i+1 < (*itertrack).second.size() && (*itertrack).second[i+1] == rightLoco) &&
                ((*itertrack).second[i]->getRightLoco() == (*itertrack).second[i+1] && (*itertrack).second[i+1]->getLeftLoco() == (*itertrack).second[i]))
            {
                (*itertrack).second[i]->setRightLoco(NULL);
                (*itertrack).second[i+1]->setLeftLoco(NULL);
                return true;
            }
        }
    }

    return false;
}

/// 连接机车
bool Track::couplingLoco(Loco* leftLoco,Loco *rightLoco)
{
    if((leftLoco == NULL || rightLoco == NULL) ||
        leftLoco == rightLoco ||
        mTrackLocos.empty())
        return false;

    // 连个结点必须相邻
    std::map<SwitchState,std::deque<Loco*>>::iterator itertrack = mTrackLocos.begin();
    for(;itertrack != mTrackLocos.end();++itertrack)
    {
        for(int i=0;i<(*itertrack).second.size();i++)
        {
            if((*itertrack).second[i] == leftLoco &&
                (i+1 < (*itertrack).second.size() && (*itertrack).second[i+1] == rightLoco) &&
                ((*itertrack).second[i]->getRightLoco() == NULL && (*itertrack).second[i+1]->getLeftLoco() == NULL))
            {
                (*itertrack).second[i]->setRightLoco((*itertrack).second[i+1]);
                (*itertrack).second[i+1]->setLeftLoco((*itertrack).second[i]);
                return true;
            }
        }
    }

    return false;
}

/// 通过轨道方向删除机车
std::vector<Loco*> Track::delLocoByTrack(int state,int dir)
{
    std::map<SwitchState,std::deque<Loco*>>::iterator itertrack = mTrackLocos.find((SwitchState)state);
    if(itertrack == mTrackLocos.end() ||
        (*itertrack).second.empty())
        return std::vector<Loco*>();

    std::vector<Loco*> decLocoArray;

    // 左边
    if(dir == 0)
    {
        decLocoArray.push_back((*itertrack).second[0]);

        Loco *nextLoco = (*itertrack).second[0]->getRightLoco();
        while(nextLoco != NULL)
        {
            decLocoArray.push_back(nextLoco);
            nextLoco = nextLoco->getRightLoco();
        }
    }
    else         // 右边
    {
        decLocoArray.push_back((*itertrack).second[(*itertrack).second.size()-1]);

        Loco *lastLoco = (*itertrack).second[(*itertrack).second.size()-1]->getLeftLoco();
        while(lastLoco != NULL)
        {
            decLocoArray.push_back(lastLoco);
            lastLoco = lastLoco->getLeftLoco();
        }

        std::reverse(decLocoArray.begin(), decLocoArray.end());
    }

    for(int i=0;i<decLocoArray.size();i++)
    {
        this->delLoco(decLocoArray[i]);
    }

    return decLocoArray;
}

/// 删除指定机车
bool Track::delLoco(Loco* ploco)
{
    if(ploco == NULL || mTrackLocos.empty())
        return false;

    std::map<SwitchState,std::deque<Loco*>>::iterator itertrack = mTrackLocos.begin();
    for(;itertrack != mTrackLocos.end();++itertrack)
    {
        std::deque<Loco*>::iterator iterloco = std::find((*itertrack).second.begin(),
                                                           (*itertrack).second.end(),
                                                           ploco);
        if(iterloco != (*itertrack).second.end())
        {
            (*iterloco)->setSectNode(NULL);
            (*itertrack).second.erase(iterloco);
            return true;
        }
    }

    return false;
}

/// 检测当前轨道是否被选中
SwitchState Track::isSelected(void)
{
    std::map<SwitchState,bool>::iterator iter = mTrackSelecteds.begin();
    for(;iter != mTrackSelecteds.end();++iter)
    {
        if((*iter).second == true)
            return (*iter).first;
    }

    return SwitchState::NOINIT;
}

/// 删除所有机车
void Track::clearAllLocos(void)
{
    mTrackLocos.clear();
}

/// 检测指定机车是否存在
bool Track::isExistLoco(Loco* ploco)
{
    if(ploco == NULL || mTrackLocos.empty())
        return false;

    std::map<SwitchState,std::deque<Loco*>>::iterator itertrack = mTrackLocos.begin();
    for(;itertrack != mTrackLocos.end();++itertrack)
    {
        std::deque<Loco*>::iterator iterloco = std::find((*itertrack).second.begin(),
                                                           (*itertrack).second.end(),
                                                           ploco);
        if(iterloco != (*itertrack).second.end())
            return true;
    }

    return false;
}

/// 设置机车占用区段
void Track::setSectNode(tagSectNode *sectnode)
{
    if(sectnode == NULL) return;

    std::map<int,wxVector<WBaseNode*>>::iterator iternode = sectnode->nodes.find((int)sectnode->curSwitchState);
    if(iternode == sectnode->nodes.end() || (*iternode).second.size() < 2)
        return;

    mTracks.clear();
    mBoundingBox.clear();
    mTrackSelecteds.clear();
    mSectNode = sectnode;

    int startNodeIndex = 0;
    int endNodeIndex = 1;

    tagMountPoint *pStartMountPoint = (*iternode).second[startNodeIndex]->getMountPointByNode((*iternode).second[endNodeIndex]);
    tagMountPoint *pEndMountPoint = (*iternode).second[endNodeIndex]->getMountPointByNode((*iternode).second[startNodeIndex]);

    if(pStartMountPoint != NULL && pEndMountPoint != NULL)
    {
        wxLine pLine;
        pLine.setPosition(pStartMountPoint->screenpos,pEndMountPoint->screenpos);

        wxPolygonEx pPloygonEx;
        pPloygonEx.append(pLine.GetNormalPoint(pLine.GetPointAtLength(0),IDD_TRACK_BOUNDINGBOX_SIZE,NormalSide::NORMAL_RIGHT));
        pPloygonEx.append(pLine.GetNormalPoint(pLine.GetPointAtLength(pLine.GetLength()),IDD_TRACK_BOUNDINGBOX_SIZE,NormalSide::NORMAL_RIGHT));
        pPloygonEx.append(pLine.GetNormalPoint(pLine.GetPointAtLength(pLine.GetLength()),IDD_TRACK_BOUNDINGBOX_SIZE,NormalSide::NORMAL_LEFT));
        pPloygonEx.append(pLine.GetNormalPoint(pLine.GetPointAtLength(0),IDD_TRACK_BOUNDINGBOX_SIZE,NormalSide::NORMAL_LEFT));

        mTracks[SwitchState::POSITIVE] = pLine;
        mBoundingBox[SwitchState::POSITIVE] = pPloygonEx;
    }

    if((*iternode).second.size() > 2)
    {
        startNodeIndex = (*iternode).second.size()-2;
        endNodeIndex = (*iternode).second.size()-1;

        pStartMountPoint = (*iternode).second[startNodeIndex]->getMountPointByNode((*iternode).second[endNodeIndex]);
        pEndMountPoint = (*iternode).second[endNodeIndex]->getMountPointByNode((*iternode).second[startNodeIndex]);

        if(pStartMountPoint != NULL && pEndMountPoint != NULL)
        {
            wxLine pLine;
            pLine.setPosition(pStartMountPoint->screenpos,pEndMountPoint->screenpos);

            wxPolygonEx pPloygonEx;
            pPloygonEx.append(pLine.GetNormalPoint(pLine.GetPointAtLength(0),IDD_TRACK_BOUNDINGBOX_SIZE,NormalSide::NORMAL_RIGHT));
            pPloygonEx.append(pLine.GetNormalPoint(pLine.GetPointAtLength(pLine.GetLength()),IDD_TRACK_BOUNDINGBOX_SIZE,NormalSide::NORMAL_RIGHT));
            pPloygonEx.append(pLine.GetNormalPoint(pLine.GetPointAtLength(pLine.GetLength()),IDD_TRACK_BOUNDINGBOX_SIZE,NormalSide::NORMAL_LEFT));
            pPloygonEx.append(pLine.GetNormalPoint(pLine.GetPointAtLength(0),IDD_TRACK_BOUNDINGBOX_SIZE,NormalSide::NORMAL_LEFT));

            mTracks[SwitchState::NEGATIVE] = pLine;
            mBoundingBox[SwitchState::NEGATIVE] = pPloygonEx;
        }
    }

    // 重置当前轨道上所有的机车位置
    std::map<SwitchState,std::deque<Loco*>>::iterator iterloco = mTrackLocos.begin();
    for(;iterloco != mTrackLocos.end();++iterloco)
    {
        if((*iterloco).second.empty())
            continue;

        wxLine pLine = mTracks[(*iterloco).first];

        // 首先得到轨道总长度
        int trackLength = pLine.GetLength()-IDD_LOCO_TWO_SPACE*2;

        std::vector<std::vector<Loco*>> decLocoArray;
        std::vector<Loco*> tmpLocoArray;

        for(int i=0;i<(*iterloco).second.size();i++)
        {
            tmpLocoArray.push_back((*iterloco).second[i]);

            if((*iterloco).second[i]->getRightLoco() == NULL &&
                (*iterloco).second[i]->getLeftLoco() == NULL)
            {
                decLocoArray.push_back(tmpLocoArray);
                tmpLocoArray.clear();

                if(i+1 == (*iterloco).second.size()-1)
                {
                    tmpLocoArray.push_back((*iterloco).second[i+1]);
                    decLocoArray.push_back(tmpLocoArray);
                    break;
                }
            }
            if((*iterloco).second[i]->getRightLoco() == NULL &&
                (*iterloco).second[i]->getLeftLoco() != NULL)
            {
                decLocoArray.push_back(tmpLocoArray);
                tmpLocoArray.clear();
            }
            else
            {
                if(i+1 == (*iterloco).second.size()-1)
                {
                    tmpLocoArray.push_back((*iterloco).second[i+1]);
                    decLocoArray.push_back(tmpLocoArray);
                    break;
                }
            }
        }

        trackLength -= ((decLocoArray.size()-1)*IDD_LOCO_SPACE);

        // 得到机车总的长度
        int totallocoLength = 0;
        for(int i=0;i<decLocoArray.size();i++)
        {
            for(int k=0;k<decLocoArray[i].size();k++)
            {
                totallocoLength+=(decLocoArray[i][k]->getName() != "" ?
                                    maindrawscene::getSingleton().getTextLength(decLocoArray[i][k]->getName()).x+2 : IDD_LOCO_LENGTH);
            }
        }

        if(totallocoLength > trackLength)
        {
            int locoLength = trackLength / (*iterloco).second.size();
            int decLocoStartLength = IDD_LOCO_TWO_SPACE;
            for(int i=0;i<decLocoArray.size();i++)
            {
                int k=0;
                for(k=0;k<decLocoArray[i].size();k++)
                {
                    wxPolygonEx *pPloygonEx = decLocoArray[i][k]->getBoundBox();
                    pPloygonEx->clear();

                    pPloygonEx->append(pLine.GetNormalPoint(pLine.GetPointAtLength(decLocoStartLength),IDD_LOCO_BOTTOM_SPACE,NormalSide::NORMAL_LEFT));
                    pPloygonEx->append(pLine.GetNormalPoint(pLine.GetPointAtLength(decLocoStartLength+locoLength),IDD_LOCO_BOTTOM_SPACE,NormalSide::NORMAL_LEFT));
                    pPloygonEx->append(pLine.GetNormalPoint(pLine.GetPointAtLength(decLocoStartLength+locoLength),25,NormalSide::NORMAL_LEFT));
                    pPloygonEx->append(pLine.GetNormalPoint(pLine.GetPointAtLength(decLocoStartLength),25,NormalSide::NORMAL_LEFT));

                    std::vector<wxPoint> allpoints = pPloygonEx->getPoints();
                    if(allpoints.size() == 4)
                    {
                        wxLine tmpline(allpoints[3],allpoints[1]);
                        decLocoArray[i][k]->setNamePosition(tmpline.GetPointAtLength(tmpline.GetLength()/2));
                    }

                    decLocoStartLength += locoLength;
                }

                decLocoStartLength += IDD_LOCO_SPACE;
            }
        }
        else
        {
            int decLocoStartLength = pLine.GetLength()/2 - (totallocoLength/2+(decLocoArray.size()-1)*IDD_LOCO_SPACE);
            for(int i=0;i<decLocoArray.size();i++)
            {
                int k=0;
                for(k=0;k<decLocoArray[i].size();k++)
                {
                    wxPolygonEx *pPloygonEx = decLocoArray[i][k]->getBoundBox();
                    pPloygonEx->clear();

                    int locoLength = (decLocoArray[i][k]->getName() != "" ?
                                        maindrawscene::getSingleton().getTextLength(decLocoArray[i][k]->getName()).x+2 : IDD_LOCO_LENGTH);

                    pPloygonEx->append(pLine.GetNormalPoint(pLine.GetPointAtLength(decLocoStartLength),IDD_LOCO_BOTTOM_SPACE,NormalSide::NORMAL_LEFT));
                    pPloygonEx->append(pLine.GetNormalPoint(pLine.GetPointAtLength(decLocoStartLength+locoLength),IDD_LOCO_BOTTOM_SPACE,NormalSide::NORMAL_LEFT));
                    pPloygonEx->append(pLine.GetNormalPoint(pLine.GetPointAtLength(decLocoStartLength+locoLength),25,NormalSide::NORMAL_LEFT));
                    pPloygonEx->append(pLine.GetNormalPoint(pLine.GetPointAtLength(decLocoStartLength),25,NormalSide::NORMAL_LEFT));

                    std::vector<wxPoint> allpoints = pPloygonEx->getPoints();
                    if(allpoints.size() == 4)
                    {
                        wxLine tmpline(allpoints[3],allpoints[1]);
                        decLocoArray[i][k]->setNamePosition(tmpline.GetPointAtLength(tmpline.GetLength()/2));
                    }

                    decLocoStartLength += locoLength;
                }

                decLocoStartLength += IDD_LOCO_SPACE;
            }
        }
    }
}

/// 更新
void Track::update(void)
{

}

/// 绘制
void Track::draw(maindrawscene *painter)
{
    if(mTracks.empty()) return;

    std::map<SwitchState,wxLine>::iterator itertrack = mTracks.begin();
    for(;itertrack != mTracks.end();++itertrack)
    {
        if(!mTrackSelecteds[(*itertrack).first])
            continue;

        painter->DrawLine((*itertrack).second.getStartPos(),
                          (*itertrack).second.getEndPos(),
                          2.0f,
                          wxColour(165, 42, 42, 255));
    }

    /*std::map<SwitchState,wxPolygonEx>::iterator iterbox = mBoundingBox.begin();
    for(;iterbox != mBoundingBox.end();++iterbox)
    {
        // 绘制包围盒
        std::vector<wxPoint> allpoints = (*iterbox).second.getPoints();
        if(allpoints.size() > 2)
        {
            for(int i=0;i<allpoints.size()-1;i++)
            {
                painter->DrawLine(allpoints[i],
                                  allpoints[i+1],
                                  1.0f,
                                  wxColour(255,0,0,255));
            }

            painter->DrawLine(allpoints[0],
                              allpoints[allpoints.size()-1],
                              1.0f,
                              wxColour(255,0,0,255));
        }
    }*/
}

/// 处理鼠标事件
bool Track::OnMMouseEvent(wxMouseEvent event)
{
    std::map<SwitchState,wxPolygonEx>::iterator iterbox = mBoundingBox.begin();
    for(;iterbox != mBoundingBox.end();++iterbox)
    {
        if((*iterbox).second.containsPoint(event.GetPosition()))
            mTrackSelecteds[(*iterbox).first] = true;
        else
            mTrackSelecteds[(*iterbox).first] = false;
    }

    return false;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Loco::Loco()
    : mSectNode(NULL),
    mLeftLoco(NULL),
    mRightLoco(NULL),
    mSelected(false)
{

}

Loco::Loco(wxString locoName)
    : mSectNode(NULL),
    mLeftLoco(NULL),
    mRightLoco(NULL),
    mLocoName(locoName),
    m_mouseOvered(false),
    mSelected(false)
{

}

Loco::~Loco()
{

}

/// 更新
void Loco::update(void)
{

}

/// 绘制
void Loco::draw(maindrawscene *painter)
{
    // 绘制包围盒
    std::vector<wxPoint> allpoints = mBoundingBox.getPoints();
    if(allpoints.size() > 2)
    {
        for(int i=0;i<allpoints.size()-1;i++)
        {
            painter->DrawLine(allpoints[i],
                              allpoints[i+1],
                              1.0f,
                              wxColour(255,255,255,255));
        }

        painter->DrawLine(allpoints[0],
                          allpoints[allpoints.size()-1],
                          1.0f,
                          wxColour(255,255,255,255));

        double decAngle = CalculateAngle(allpoints[0],allpoints[1]);
        if(decAngle > 0.0f)
            decAngle = 360.0f - decAngle;

        wxString decLocoName = this->getName();
        double locoframelength = wxLine(allpoints[0],allpoints[1]).GetLength();
        double loconamelength = painter->getTextLength(decLocoName).x;

        if(loconamelength > locoframelength)
        {
            if(locoframelength > painter->getTextLength("...").x)
                decLocoName = "...";
            else
                decLocoName = "";
        }

        painter->DrawText4(decLocoName.ToUTF8(),
                           mLocoNamePos,
                           wxColour(255,255,255,255),
                           decAngle,
                           2);
    }
}

/// 处理鼠标事件
bool Loco::OnMMouseEvent(wxMouseEvent event)
{
    if(mBoundingBox.containsPoint(event.GetPosition()))
    {
        m_mouseOvered = true;
        mSelected = true;
        m_mousePosition = event.GetPosition();
    }
    else
    {
        m_mouseOvered = false;
        mSelected = false;
    }

    return false;
}
